import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
// import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js'
// import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
// 模型引入obj模型的纹理引入
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import JSZip from 'jszip'
export default class ThreeBase {
  constructor(el, options, loadFunc) {
    this.dom = el // 插入的dom
    // 配置参数
    this.opt = {
      sceneUrl: '', // 场景url
      modelUrl: '', // 模型url
      model: '',
      autoRotate: false, // 是否自动旋转
      isFullBrowser: true, // 生成的canvas是否铺满浏览器
      ...options
    }
    this.scene = null // 场景
    this.camera = null // 摄像机
    this.renderer = null // 渲染器
    this.controls = null // 控制器
    this.model = null // 模型
    this.atask = null
    this.controller = null // 下载器
    this.loadFunc = loadFunc
    this.loadFunc(0)
    this.init()
  }
  // 初始化
  init() {
    this.sceneInit()
    this.cameraInit()
    this.renderInit()
    this.controlsInit()
    this.animate()
    // 加载场景
    if (this.opt.sceneUrl) {
      this.loadScene(this.opt.sceneUrl)
    }
    // 加载模型
    if (this.opt.modelUrl) {
      this.loadModel(this.opt.modelUrl)
    }
    // 响应窗口大小改变
    if (this.opt.isFullBrowser) {
      window.addEventListener('resize', this.onWindowResize.bind(this))
    }
  }

  reset() {
    this.controlsRotate(false)
    this.controls.reset()
    this.adjustModel(this.model)
    setTimeout(() => {
      this.controlsRotate(this.opt.autoRotate)
    }, 1000)
  }

  // 场景初始化
  sceneInit() {
    this.scene = new THREE.Scene()
    // 环境光
    let ambient = new THREE.AmbientLight(new THREE.Color(0xffffff))
    this.scene.add(ambient)

    // var e = new THREE.SpotLight(new THREE.Color(0xffffff))
    // e.position.set(20, 0, 0)
    // e.castShadow = !0
    // e.receiveShadow = !0
    // this.scene.add(e)
  }
  // 相机初始化
  cameraInit() {
    this.camera = new THREE.PerspectiveCamera(
      95, // 摄像机视锥体垂直视野角度
      this.getWidth() / this.getHeight(), // 摄像机视锥体长宽比
      0.01, // 摄像机视锥体近端面
      1000 // 摄像机视锥体远端面
    )
    this.camera.position.z = 50
    this.camera.position.set(0, 0, 1)
  }
  // 渲染器初始化
  renderInit() {
    this.renderer = new THREE.WebGLRenderer({
      antialias: true, // 抗锯齿
      alpha: true // canvas是否包含alpha
    })
    this.renderer.setSize(this.getWidth(), this.getHeight())
    this.renderer.setPixelRatio(window.devicePixelRatio)
    this.dom.appendChild(this.renderer.domElement)
  }
  // 控制器初始化
  controlsInit() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
   
    if (this.autoRotate) {
      this.controls.autoRotateSpeed = 2.0 // 自动旋转速度
    } else {
      this.controls.autoRotateSpeed = 0 // 自动旋转速度
    }
    this.controls.autoRotate = this.autoRotate // 是否自动转动
    this.controls.enableDamping = true // 是否惯性滑动
    this.controls.dampingFactor = 0.2
    this.controls.rotateSpeed = 0.5 // 手动旋转速度
    // to disable zoom
    // this.controls.enableZoom = false
    // to disable rotation
    // this.controls.enableRotate = false
    // to disable 移动
    this.controls.enablePan = false
    // to disable zoom
    // this.controls.enableZoom = false
    // this.controls.maxZoom = 2
    // this.controls.minPolarAngle = .25 * Math.PI,
    // this.controls.maxPolarAngle = .75 * Math.PI,
    // this.controls.minAzimuthAngle = 0 * Math.PI,
    // this.controls.maxAzimuthAngle = 1 * Math.PI

    this.controlsRotate(this.opt.autoRotate)
    this.controls.update()
  }
  // 控制器旋转
  controlsRotate(autoRotate) {
    console.log('autoRotate', autoRotate)
    this.controls.autoRotate = autoRotate || false
    if (autoRotate) {
      this.controls.autoRotateSpeed = 2.0 // 自动旋转速度
    } else {
      this.controls.autoRotateSpeed = 0 // 自动旋转速度
    }
    if (autoRotate) {
      // 停止操作3s后继续自动转动
      let timer = null
     
      this.controls.addEventListener('start', () => {
        if (timer) {
          clearTimeout(timer)
        }
        this.controls.autoRotate = false
        
        this.visibleSize(false)
      })
      this.controls.addEventListener('end', () => {
        timer = setTimeout(() => {
          console.log('autoRotate end', autoRotate)
         
          this.controls.autoRotate = autoRotate
         
          this.visibleSize(true)
          this.controls.update()
        }, 1000)
      })
    }
  }
  // 动画渲染
  animate() {
    this.atask = requestAnimationFrame(() => {
      this.animate()
    })
    this.controls.update()
    this.renderer.render(this.scene, this.camera)
  }
  // 加载场景
  loadScene(url) {
    if (/\.hdr$/i.test(url)) {
      new RGBELoader().load(url, texture => {
        texture.mapping = THREE.EquirectangularReflectionMapping
        this.scene.background = texture
        this.scene.environment = texture
      })
    } else if (/\.(jpg|jpeg|png|gif|bmp)$/i.test(url)) {
      new THREE.TextureLoader().load(url, texture => {
        texture.mapping = THREE.EquirectangularReflectionMapping
        texture.wrapS = THREE.RepeatWrapping
        texture.wrapT = THREE.RepeatWrapping
        this.scene.background = texture
        this.scene.environment = texture
      })
    }
  }
  visibleSize(flag = false) {
    if (!(this.model && this.model.children && this.model.children.length > 0 && this.model.children[0].children && this.model.children[0].children.length > 0)) {
      return
    }
    let obj = this.model.children[0].children.find(d => d.name == 'Size')
    console.log('obj', obj)
    if (obj) {
      obj.visible = flag
      const color = new THREE.Color(0xffe38a)
      obj.children.filter(d => d.isMesh).forEach(d => {
        d.material.color = color
      })
    }
  }

  // 销毁 Three.js 对象和资源
  destroy() {
    console.log('destroy')
    // 停止渲染循环
    cancelAnimationFrame(this.atask);
    if (this.controller) {
      // 取消下载 
      this.controller.abort();
   }

    // 移除窗口大小改变事件监听器
    if (this.opt.isFullBrowser) {
      window.removeEventListener('resize', this.onWindowResize.bind(this));
    }
    if (this.controls) {
      this.controls.removeEventListener('start')
      this.controls.removeEventListener('end')
    }

    // 移除模型
    if (this.model) {
      this.scene.remove(this.model);

      // 释放模型的资源（如果适用）
      if (this.model.geometry) {
        this.model.geometry.dispose();
      }
      if (this.model.material) {
        if (Array.isArray(this.model.material)) {
          this.model.material.forEach(material => material.dispose());
        } else {
          this.model.material.dispose();
        }
      }
    }

    // 销毁渲染器
    if (this.renderer) {
      this.renderer.dispose();
    }

    // 其他资源释放操作...

    // 重置变量为 null
    this.scene = null;
    this.camera = null;
    this.renderer = null;
    this.controls = null;
    this.model = null;
  }

  // 加载模型
  loadModel(url) {
    console.log('加载模型', url)

    if ((this.opt.model && this.opt.model == 'glb') || (/\.gltf$/i.test(url) || /\.glb$/i.test(url))) {
      const loadingManager = new THREE.LoadingManager()
      loadingManager.onProgress = (url, itemsLoaded, itemsTotal) => {
        console.log('Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.')
        console.log(itemsLoaded / itemsTotal)
      }
      // 当所有资源加载完成后执行
      loadingManager.onLoad = () => {
        console.log('All resources loaded!')
        // 在这里执行其他操作
        this.loadFunc(100)
      }
      let loader = new GLTFLoader(loadingManager)
      // const dracoLoader = new DRACOLoader() //
      // dracoLoader.setDecoderPath('/static/draco/gltf/') // 设置解压库文件路径
      // dracoLoader.setDecoderConfig({ type: 'js' })  // 使用js方式解压
      // dracoLoader.onProgress = (url, itemsLoaded, itemsTotal) => {
      //   console.log('解压===Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.')
      // }
      // dracoLoader.preload()  // 初始化_initDecoder 解码器
      // loader.setDRACOLoader(dracoLoader)   // gltfloader使用dracoLoader
      // GLTF模型加载
      loader.load(
        url,
        gltf => {
          if (this.model) {
            this.scene.remove(this.model)
          }
          // 缩放
          // this.scene.scale.set(.5, .5, .5)
          let model = gltf.scene
          this.adjustModel(model)
          this.model = model
          console.log(model)
          this.scene.add(this.model)
          // var t = new THREE.AmbientLight(new THREE.Color(0xffffff), .2)
          // this.scene.add(t)
          this.visibleSize(true)

        },
        (xhr, a, b) => {
          if (xhr.loaded / xhr.total != 1) {
            this.loadFunc((xhr.loaded / xhr.total * 100))
          }
          console.log('====', xhr, a, b)
        },
        error => {
          console.error('模型加载失败:', error)
        }
      )
    } else if ((this.opt.model && this.opt.model == 'zip') || /\.zip$/i.test(url)) {
      // zip
      this.loadFunc(0)
      this.downloadFile(url, loaded => {
        this.loadFunc(loaded)
      })
        .then(data => {
          if (!data) {
						return
					}
          JSZip.loadAsync(data).then(zip => {
            console.log(zip.files)
            // 提取OBJ文件
            let jpgPath = null;
            let mtlPath = null;
            let objPath = null;
            zip.forEach((relativePath) => {
              if (!jpgPath && /\.jpg$/i.test(relativePath)) {
                // 这里可以处理匹配的文件
                console.log('Matching file found: ' + relativePath);
                jpgPath = relativePath
              }
              if (!mtlPath && /\.mtl$/i.test(relativePath)) {
                // 这里可以处理匹配的文件
                console.log('Matching file found: ' + relativePath);
                mtlPath = relativePath
              }
              if (!objPath && /\.obj$/i.test(relativePath)) {
                // 这里可以处理匹配的文件
                console.log('Matching file found: ' + relativePath);
                objPath = relativePath
              }
            });
            if (this.model) {
              this.scene.remove(this.model)
            }
            var mtlLoader = new MTLLoader()
            var objLoader = new OBJLoader()
            // 提取MTL文件
            zip.file(mtlPath).async('string').then(mtlText => {
              var materials = mtlLoader.parse(mtlText);
              materials.preload();
              console.log(materials)
              // objLoader.setMaterials(materials)
              zip.file(objPath).async('string').then(objText => {
                var obj = objLoader.parse(objText);

                // 提取JPG文件
                zip.file(jpgPath).async('arraybuffer').then(imageData => {
                  console.log('imageData', imageData)
                  var textureLoader = new THREE.TextureLoader();
                  var texture = textureLoader.load(URL.createObjectURL(new Blob([imageData]))); // 设置新的纹理图片路径
                  console.log('texture', texture)
                  // 设置模型的材质
                  obj.traverse(function (child) {
                    if (child instanceof THREE.Mesh) {
                      child.material.map = texture;
                    }
                  });
                  this.loadFunc(100)
                  this.adjustModel(obj)
                  this.model = obj
                  this.scene.add(this.model)
                });
              });
            });
          });
        });
    } else if ((this.opt.model && this.opt.model == 'obj') || /\.obj$/i.test(url)) {
      var mtlLoader = new MTLLoader()
      // OBJ模型加载
      let mtlurl = url.replace('.obj', '.mtl')
      let jpgurl = url.substr(0, url.lastIndexOf('/'))
      console.log('jpgurl', jpgurl)
      mtlLoader.setPath(jpgurl + '/')
      // mtlLoader.setTexturePath(jpgurl) // 材质加载路径
      mtlLoader.load(mtlurl.substr(url.lastIndexOf('/'), mtlurl.length), materials => {
        materials.preload()
        var objLoader = new OBJLoader()
        objLoader.setMaterials(materials)
        objLoader.load(
          url,
          obj => {
            if (this.model) {
              this.scene.remove(this.model)
            }
            obj.traverse(function (child) {
              console.log(child)
              if (child.isMesh) {
                child.castShadow = true
                child.material.emissive = child.material.color
                child.material.emissiveMap = child.material.map
              }
            })

            // this.scene.scale.set(.4, .4, .4)
            this.adjustModel(obj)
            this.model = obj
            this.scene.add(this.model)
          },
          xhr => {
            this.loadFunc((xhr.loaded / xhr.total * 100))
            // console.log((xhr.loaded / xhr.total * 100) + '% loaded')
          },
          error => {
            console.error('模型加载失败:', error)
          }
        )
      })
    } else if ((this.opt.model && this.opt.model == 'fbx') || /\.fbx$/i.test(url)) {
      // FBX模型加载
      new FBXLoader().load(
        url,
        obj => {
          if (this.model) {
            this.scene.remove(this.model)
          }
          this.adjustModel(obj)
          this.model = obj
          this.scene.add(this.model)
        },
        xhr => {
          console.log((xhr.loaded / xhr.total * 100) + '% loaded');
        },
        error => {
          console.error('模型加载失败:', error)
        }
      )
    }
  }
  // 根据模型调整相机position
  adjustModel(model) {
    // model.updateMatrixWorld();
    let box3 = new THREE.Box3().setFromObject(model)
    let size = new THREE.Vector3()
    box3.getSize(size)
    // 将物体居中到原点
    const center = new THREE.Vector3()
    box3.getCenter(center)
    model.position.sub(center)
    model.position.y = -(size.y / 3)
    let distance = (size.x + size.y + size.z) / 3
    console.log('distance', distance)
    if (distance < 8) {
      distance = 8
    }
    this.controls.minDistance = distance / 2 // 最小距离
    this.controls.maxDistance = distance * 2 // 最大距离

    this.camera.position.set(0, 0, distance)

    this.scene.scale.set(.8, .8, .8)

    this.camera.updateProjectionMatrix()
  }
  // 响应窗口大小
  onWindowResize() {
    this.camera.aspect = this.getWidth() / this.getHeight()
    this.camera.updateProjectionMatrix()
    this.renderer.setSize(this.getWidth(), this.getHeight())
  }
  // 获取宽度
  getWidth() {
    return this.opt.isFullBrowser ? window.innerWidth : this.dom.offsetWidth
  }
  // 获取高度
  getHeight() {
    return this.opt.isFullBrowser ? window.innerHeight : this.dom.offsetHeight
  }
  // 下载
  async downloadFile(url, callback) {
    try {
      this.controller = new AbortController();
      var signal = this.controller.signal;
      // 取消下载 this.controller.abort();
      const response = await fetch(url, { signal });
      if (!response.ok) {
        throw new Error('Download failed');
      }

      const contentLength = response.headers.get('content-length');
      const totalBytes = parseInt(contentLength, 10);

      const reader = response.body.getReader();
      let receivedBytes = 0;
      const chunks = [];
      let run = true
      while (run) {
        const { done, value } = await reader.read();

        if (done) {
          run = false;
          this.controller = null
          break;
        }

        receivedBytes += value.byteLength;
        const progress = (receivedBytes / totalBytes) * 100;

        // 在这里你可以更新下载进度
        console.log(`Download progress: ${progress.toFixed(2)}%`);
        callback(progress)
        chunks.push(value);
      }

      const blob = new Blob(chunks, { type: response.headers.get('content-type') });
      return blob
    } catch (error) {
      this.controller = null
      return false
    }
  }
}
